/* Custom detection.  The below is just a base for generating the stock stuff.
alert tcp $EXTERNAL_NET any -> $HOME_NET $HTTP_PORTS (msg:"DOS generic web server hashing collision attack"; flow:to_server,established,only_stream; content:"Content-Length|3a| "; nocase; http_header; reference:cve,2011-3414; reference:url,events.ccc.de/congress/2011/Fahrplan/events/4680.en.html; reference:url,technet.microsoft.com/en-us/security/advisory/2659883; classtype:attempted-dos; sid:20825;)


The basic idea here is we are counting the '=' in POST data and if we have more than MAX_POST_PARAMS (defined below) parameters, we alert.

*/
/*
 * Vuln Title: XXXX
 *
 * Copyright (C) 2005-2010 Sourcefire, Inc. All Rights Reserved
 *
 * Written by XXXX, Sourcefire VRT <XXXX@sourcefire.com>
 *
 * Auto-generated by XXXX
 *
 * This file may contain proprietary rules that were created, tested and
 * certified by Sourcefire, Inc. (the "VRT Certified Rules") as well as
 * rules that were created by Sourcefire and other third parties and
 * distributed under the GNU General Public License (the "GPL Rules").  The
 * VRT Certified Rules contained in this file are the property of
 * Sourcefire, Inc. Copyright 2005 Sourcefire, Inc. All Rights Reserved.
 * The GPL Rules created by Sourcefire, Inc. are the property of
 * Sourcefire, Inc. Copyright 2002-2005 Sourcefire, Inc. All Rights
 * Reserved.  All other GPL Rules are owned and copyrighted by their
 * respective owners (please see www.snort.org/contributors for a list of
 * owners and their respective copyrights).  In order to determine what
 * rules are VRT Certified Rules or GPL Rules, please refer to the VRT
 * Certified Rules License Agreement.
 */

#include "sf_snort_plugin_api.h"
#include "sf_snort_packet.h"

//#define DEBUG
#ifdef DEBUG
#define DEBUG_SO(code) code
#else
#define DEBUG_SO(code)
#endif


/* declare detection functions */
int rule20825eval(void *p);

/* declare rule data structures */
/* flow:established, only_stream, to_server; */
static FlowFlags rule20825flow0 = 
{
    FLOW_ESTABLISHED|FLOW_ONLY_REASSMBLED|FLOW_TO_SERVER
};

static RuleOption rule20825option0 =
{
    OPTION_TYPE_FLOWFLAGS,
    {
        &rule20825flow0
    }
};
#ifndef CONTENT_BUF_HEADER
#define CONTENT_BUF_HEADER CONTENT_BUF_NORMALIZED
#endif
// content:"Content-Length|3a| ", payload http_header, depth 0, nocase, fast_pattern; 
static ContentInfo rule20825content1 = 
{
    (uint8_t *) "Content-Length|3a| ", /* pattern (now in snort content format) */
    0, /* depth */
    0, /* offset */
    CONTENT_NOCASE|CONTENT_FAST_PATTERN|CONTENT_BUF_HEADER, /* flags */
    NULL, /* holder for boyer/moore PTR */
    NULL, /* more holder info - byteform */
    0, /* byteform length */
    0 /* increment length*/
};

static RuleOption rule20825option1 = 
{
    OPTION_TYPE_CONTENT,
    {
        &rule20825content1
    }
};

/* references for sid 20825 */
/* reference: cve "2011-3414"; */
static RuleReference rule20825ref1 = 
{
    "cve", /* type */
    "2011-3414" /* value */
};

/* reference: url "events.ccc.de/congress/2011/Fahrplan/events/4680.en.html"; */
static RuleReference rule20825ref2 = 
{
    "url", /* type */
    "events.ccc.de/congress/2011/Fahrplan/events/4680.en.html" /* value */
};

/* reference: url "technet.microsoft.com/en-us/security/advisory/2659883"; */
static RuleReference rule20825ref3 = 
{
    "url", /* type */
    "technet.microsoft.com/en-us/security/advisory/2659883" /* value */
};

/* reference: url "technet.microsoft.com/en-us/security/bulletin/MS11-100"; */
static RuleReference rule20825ref4 =
{
    "url", /* type */
    "technet.microsoft.com/en-us/security/bulletin/MS11-100" /* value */
};

/* reference: cve "2012-0830"; */
static RuleReference rule20825ref5 =
{
    "cve", /* type */
    "2012-0830" /* value */
};

/* reference: cve "2010-1899"; */
static RuleReference rule20825ref6 =
{
    "cve", /* type */
    "2010-1899" /* value */
};

/* reference: cve "2011-5037"; */
static RuleReference rule20825ref7 =
{
    "cve", /* type */
    "2011-5037" /* value */
};


static RuleReference *rule20825refs[] =
{
    &rule20825ref1,
    &rule20825ref2,
    &rule20825ref3,
    &rule20825ref4,
    &rule20825ref5,
    &rule20825ref6,
    &rule20825ref7,
    NULL
};
/* metadata for sid 20825 */
/* metadata:; */

static RuleMetaData rule20825service1 =
{
   "service http"
};

static RuleMetaData rule20825policy1 = 
{
   "policy max-detect-ips drop"
};

static RuleMetaData *rule20825metadata[] =
{
    &rule20825service1,
    &rule20825policy1,
    NULL
};

RuleOption *rule20825options[] =
{
    &rule20825option0,
    &rule20825option1,
    NULL
};

Rule rule20825 = {
   /* rule header, akin to => tcp any any -> any any */
   {
       IPPROTO_TCP, /* proto */
       "$EXTERNAL_NET", /* SRCIP     */
       "any", /* SRCPORT   */
       0, /* DIRECTION */
       "$HOME_NET", /* DSTIP     */
       "$HTTP_PORTS", /* DSTPORT   */
   },
   /* metadata */
   { 
       3,  /* genid */
       20825, /* sigid */
       11, /* revision */
       "attempted-dos", /* classification */
       0,  /* hardcoded priority XXX NOT PROVIDED BY GRAMMAR YET! */
       "SERVER-WEBAPP generic web server hashing collision attack",     /* message */
       rule20825refs /* ptr to references */
       ,rule20825metadata
   },
   rule20825options, /* ptr to rule options */
   &rule20825eval, /* use the built in detection function */
   0 /* am I initialized yet? */
};


#define MAX_POST_PARAMS 250
#define JUMP_DIST 3 // 1 char name, '&', '=' (formerly 9, changed for php dos) six character name (shorter than real attacks) + '&' + '=' + 1-byte value/* detection functions */
int rule20825eval(void *p) {
   const uint8_t *cursor_normal = 0;
   const uint8_t *cursor_http_header = 0;
   SFSnortPacket *sp = (SFSnortPacket *) p;
   const uint8_t *end_of_payload;
   uint32_t paramcount = 0;

   DEBUG_SO(fprintf(stderr, "rule20825eval enter\n");)

   if(sp == NULL)
      return RULE_NOMATCH;

   if(sp->payload == NULL)
      return RULE_NOMATCH;
   
   // flow:established, to_server;
   if(checkFlow(p, rule20825options[0]->option_u.flowFlags) <= 0)
      return RULE_NOMATCH;

// This is commented out because it's the fast_pattern and therefore was found before entering.
//   // content:"Content-Length|3a| ", payload http_header, depth 0, nocase, fast_pattern;
//   if(contentMatch(p, rule20825options[1]->option_u.content, &cursor_http_header) <= 0)
//      return RULE_NOMATCH;

   // Now, let's grab the POST data (http_client_body)
   if(getBuffer(sp, CONTENT_BUF_POST, &cursor_normal, &end_of_payload) <= 0)
      return RULE_NOMATCH;

   if(cursor_normal == NULL)  // ZDNOTE temporary fix for getBuffer() bug
      return RULE_NOMATCH;

   // Verify there is at least a minimum amount of data available
   DEBUG_SO(fprintf(stderr, "We have %d bytes of data\n", (int)(end_of_payload - cursor_normal));)
   if(end_of_payload - cursor_normal < MAX_POST_PARAMS * JUMP_DIST)
      return RULE_NOMATCH;
   
   // Count the '=' characters, which we are using as an indicator of how many parameters there are
   // We don't use any stored stream data here because with PAF and default values (currently 14k-ish)
   // we should get everything as one giant chunk and not have to worry about carrying counts forward.
   while(cursor_normal < end_of_payload) {
      DEBUG_SO(fprintf(stderr, "%c", *cursor_normal);)

      if(*cursor_normal == '=') {
         paramcount++;
         DEBUG_SO(fprintf(stderr, "Found '=' (%d)\n", paramcount);)
      
         // If there are too many parameters, alert!
         if(paramcount >= MAX_POST_PARAMS)
            return RULE_MATCH;

         cursor_normal += JUMP_DIST; // Cheating to skip as many checks as possible
      } else {
         cursor_normal++; // Try again looking for a '='
      }
   }

   return RULE_NOMATCH;
}
/*
Rule *rules[] = {
    &rule20825,
    NULL
};
*/
